/*-----------------------------------------------------------------------------
	6502 Macroassembler and Simulator
		additions
Copyright (C) 2023 Mark Spankus

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-----------------------------------------------------------------------------*/

#include "StdAfx.h"
#include "simOSI.h"
#include "resource.h"
#include "broadcast.h"
#include <memory.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define MAX_ROWS 32
#define MAX_COLS 64

////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// BOOL MakeRectVisible(RECT& rect)
//
// Adjust coordiates of rect to fit on screen if possible
//
// Input: pointer to rect to be displayed
// Output: Adjusted Rect
// Returns:
//         BOOL could rect fit on screen?
//
/////////////////////////////////////////////////////////////////////////////
BOOL MakeRectVisible(RECT& rRect)
{
    RECT rSystem;
	BOOL bChanged = FALSE;
    int rWidth, rHeight, abit;
    if (rRect.bottom < rRect.top)  //normalize rect
    {
		int nTemp = rRect.bottom;
		rRect.bottom = rRect.top;
		rRect.top = nTemp;
    }
    if (rRect.right < rRect.left)
    {
		int nTemp = rRect.right;
		rRect.right = rRect.left;
		rRect.left = nTemp;
    }

    
    //Get virtual desktop if multimonitor else fullscreen size
    rSystem.left = ::GetSystemMetrics(SM_XVIRTUALSCREEN);
    rSystem.top  = ::GetSystemMetrics(SM_YVIRTUALSCREEN);
    rSystem.right  = rSystem.left + ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
    rSystem.bottom = rSystem.top  + ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
    
    if (IsRectEmpty(&rSystem) )
    {
        //just to be sure
        rSystem.top = 0;
        rSystem.left = 0;
        rSystem.right = rSystem.left + ::GetSystemMetrics(SM_CXFULLSCREEN);
        rSystem.bottom = rSystem.top + ::GetSystemMetrics(SM_CYFULLSCREEN);
    }
    rWidth = RectWidth(rRect);
    rHeight = RectHeight(rRect);
    abit   = ::GetSystemMetrics(SM_CYMENU);

    //rRect->NormalizeRect();

    //Is some point of upper-left corner of window on virtual desktop?
    //POINT tl = {rRect.left, rRect.top};
	//POINT br = {rRect.right, rRect.bottom};
	POINT mid = { rRect.left + rWidth/2, rRect.top + rHeight/2};

    //is rect visible on any monitor?
	//if ( MonitorFromPoint(tl, MONITOR_DEFAULTTONULL) && MonitorFromPoint(br, MONITOR_DEFAULTTONULL)  ) 
	//	return FALSE;//nothing changed

	//nope, move it to some monitor
	HMONITOR hMon = MonitorFromRect( &rRect, MONITOR_DEFAULTTONULL);
	if (!hMon)
		hMon = MonitorFromPoint( mid, MONITOR_DEFAULTTONEAREST);
	if (hMon)
	{
		//rect may be on virtual desktop but is not on screen
		MONITORINFO monInfo;

		ZeroMemory( &monInfo, sizeof(MONITORINFO));
		monInfo.cbSize = sizeof(MONITORINFO);
		if (GetMonitorInfo(hMon, &monInfo))
		{
			rSystem = monInfo.rcWork;
		}

	}
    

                

    if (rRect.bottom > rSystem.bottom)
    {
        //slide bottom edge on screen plus a bit
        rRect.top = rSystem.bottom - abit - rHeight;
		rRect.bottom = rRect.top + rHeight;
		bChanged = TRUE;
    }

    if (rRect.top < rSystem.top)
    {
        //slide top onto screen
		rRect.bottom = rSystem.top + rHeight; 
        rRect.top = rSystem.top;
		bChanged = TRUE;
                
    }
    if (rRect.right > rSystem.right)
    {
        //slide right edge onscreen
        rRect.left  = rSystem.right - rWidth;
        rRect.right = rSystem.right;
		bChanged = TRUE;
    }
    if (rRect.left < rSystem.left)
    {
        rRect.right = rSystem.left + rWidth;
        rRect.left = rSystem.left;
		bChanged = TRUE;
    }

    
    if ( rWidth > RectWidth(rSystem) || rHeight > RectHeight(rSystem))
	{
		rRect.right = rSystem.right;
		rRect.bottom = rSystem.bottom;
        return FALSE;    //too big to fit on screen
	}
    
    return bChanged;

}
BOOL MakeRectSized(RECT& rRect, int minx, int miny, int l, int r, int w, int h)
{
	BOOL bSet = FALSE;
	if (rRect.bottom - rRect.top < miny)
	{
	    rRect.bottom = rRect.top + h;
		bSet = TRUE;
	}
	if (rRect.right - rRect.left < minx)
	{
		rRect.right = rRect.left + w;
		bSet = TRUE;
	}
	if (bSet)
		OffsetRect(&rRect, l, r);

	MakeRectVisible(rRect);
	return !bSet;		
	
}

int load_resource(LPCTSTR name, UINT nID, BYTE *addr, DWORD length )
{
	
	int nReturn = -1;
	HINSTANCE hInst = AfxGetResourceHandle();
	HRSRC hResource = FindResource( hInst, MAKEINTRESOURCE(nID), name);
	if (hResource)
	{	
		ULONG uReSize = SizeofResource( hInst, hResource);
		if (!addr)
			return (int) uReSize;
		
		if (uReSize > 0 && uReSize < 64 * 1024)
		{	
			HGLOBAL resource = LoadResource( hInst, hResource);
			if (resource)
			{
				LPBYTE pData = (LPBYTE) LockResource( resource);

				nReturn = min(length, (int) uReSize);
				memcpy(addr, pData, nReturn); 
				//obsolete UnLockResource(resource);
			 }
		}
	}

	return nReturn;

}


HANDLE LoadFont(UINT uRes, const TCHAR * szFontName)
{
	
	TCHAR szFontFile[255] = {0};
	HGLOBAL hMemResource = NULL;
	HANDLE hFontTTF = NULL;
	HFONT hFont = NULL;
	
	if (szFontName)
	{
		_tcsncpy(szFontFile, szFontName, 254);
		_tcscat(szFontFile, _T(".TTF"));
	}
	
	BOOL bFontInstalled = IsFontAvailable(szFontName, NULL);
	
	if (!bFontInstalled)
	{
		int nSize = load_resource(_T("TRUETYPE"), uRes, NULL, 0 );
		if (nSize)
		{
			hMemResource = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, nSize);
			unsigned char* pBuf = (unsigned char*) ::GlobalLock(hMemResource);
			if (pBuf)
			{
				DWORD dwNum = 1;
				nSize = load_resource(_T("TRUETYPE"), uRes, pBuf, nSize );
			    hFontTTF = AddFontMemResourceEx(pBuf, nSize, 0, &dwNum);
			}

		}
	}

   return hFontTTF;
}

static int CALLBACK FontAvailableCallBack( ENUMLOGFONT FAR * , //lpelf,  // pointer to logical-font data 
						NEWTEXTMETRIC FAR * , // lpntm, // pointer to physical-font data 
						int , //FontType, // type of font 
						LPARAM lpb
)
{
	ASSERT( lpb); // lpb is pointer to bYes in isFontAvailable
	* (bool *) lpb = true;
	return 0;	/* stop enumeration, that's all we need to know */
}


BOOL CompareFonts(
    const TEXTMETRIC *ptm,
    const TCHAR *pFaceName,
    const LOGFONT lf
    )
//
//  Comparisons made in roughly the same order that the font mapper
//  weights any given font.
//
//  See Technical Article: "Windows Font Mapping" by Ron Gery
//
{
    BOOL bTTRequired;
    BOOL bTTRealized;

    // Compare character sets if they were specified;
    // if character sets don't compare, then fonts are not
    // even similar.
    if (lf.lfCharSet != DEFAULT_CHARSET)
    {
        if (lf.lfCharSet != ptm->tmCharSet) return FALSE;
    }

    // Compare pitch if the pitch bits are set.
    // Note counter-intuitive nature of TextMetric.tmPitchAndFamily,
    // see Platform SDK docs.
    if (lf.lfPitchAndFamily & 0x03)
    {
        BOOL bFixedPitchRequired = (lf.lfPitchAndFamily & FIXED_PITCH) > 0;
        BOOL bFixedPitchRealized = !((ptm->tmPitchAndFamily & TMPF_FIXED_PITCH) > 0);

        if ( bFixedPitchRequired != bFixedPitchRealized )
            return FALSE;
    }

    // Compare TT flags.
    // Was TrueType only requested?
    bTTRequired = (lf.lfOutPrecision & OUT_TT_ONLY_PRECIS) > 0;

    if (bTTRequired)
    {
        // Is the realized font TT?
        bTTRealized = (ptm->tmPitchAndFamily & TMPF_TRUETYPE) > 0;

        // Do they match?
        if ( bTTRequired != bTTRealized )
            return FALSE;
    }

    // Compare only if a facename was supplied.
    if ( lstrlen( lf.lfFaceName ) > 0 )
    {
        // NOTE: a stricter comparison would be case-sensitive.
        if ( lstrcmpi( pFaceName, lf.lfFaceName ) != 0 )
            return FALSE;
    }

    if ((BOOL)(lf.lfItalic) != (BOOL)(ptm->tmItalic)) return FALSE;

    // This is sufficient to determine most "successful" font mappings.
    // Even more restrictive comparisons can compare:
    //      Type Families via lf.lfPitchAndFamily
    //      Font technology: Device, Raster, TrueType, and so on
    //      Height: Realized size equals or "close" to LOGFONT request
    //      Width
    //      Weight
    //      Underline
    //      Strikeout


    return TRUE;

} // End of function CompareFonts. 

BOOL FontMappingSuccess(const HDC hdc)
{
    BOOL                bMatched = FALSE;
    TEXTMETRIC          tm;
    LOGFONT             lf;
    TCHAR   szFaceName[LF_FACESIZE];

    ZeroMemory(&lf, sizeof(lf));
	ZeroMemory(&szFaceName, sizeof(szFaceName));

	HGDIOBJ hFont = GetCurrentObject( hdc, OBJ_FONT );
	if (hFont)
		if (GetObject( hFont ,  sizeof(lf), (LPVOID) &lf ) == 0)
			return FALSE;

    // Get the info for the currently realized font.
    GetTextMetrics( hdc, &tm );
    if (!GetTextFace( hdc, sizeof(szFaceName), szFaceName ))
		szFaceName[0] = _T('\0');

    // Compare realized to logical font description to
    // determine mapping success.
    bMatched = CompareFonts( &tm, szFaceName, lf );

    return bMatched;

} // End of function FontMappingSuccess.


//Fonts added with AddFontMemResourceEx are not enumerable
bool IsFontAvailable( LPCTSTR const pFontName, HDC hDC /* = 0 */)
{
	bool bYes = false; // will get set to true in callback above
	HDC hDc2Use = hDC;
	if (! hDC)
		hDc2Use = CreateDC( _T("DISPLAY"),0,0,0);
	if (hDc2Use)		
	{	
		/*::EnumFontFamilies( hDc2Use,
							pFontName,
							(FONTENUMPROC) FontAvailableCallBack,
							(LPARAM) &bYes);
		*/
		bYes = true; //assume true for nonenumerable fonts
		if (bYes)
		{
				
			HFONT hFont;

			hFont = CreateFont(-18, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, 
					OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 
					DEFAULT_PITCH , pFontName);
			if (hFont)
			{
				HGDIOBJ holdFont = ::SelectObject(hDc2Use, hFont);

				//okay do we really really have the font? Compare what font mapper gave us
				if (!FontMappingSuccess(hDc2Use))
					bYes = false;

				SIZE size = {0,0};

				//okay do we really really really have a font? Let's try to use it!
				if (bYes && !GetTextExtentPoint32(hDc2Use, _T("ABCDEFGH"), 8, &size))
					bYes = false;  //AHA! its GONE Win2000 this fails if font has been deleted

				::SelectObject(hDc2Use, holdFont);

				DeleteObject(hFont);
			}
			else
			{
				//well, if we can't create it, something must not be right!
				bYes = false;
			}

		}
		
		if (! hDC)
			DeleteDC( hDc2Use);
	}
	return bYes;
}

bool addFont( const TCHAR * szFontPath)
{
	bool bOK = false;
	if (szFontPath && *szFontPath)
	{
		bOK = ::AddFontResource( szFontPath) != 0;
		if (bOK) // alert everybody that a new font is available
		//PostMessage( HWND_BROADCAST, WM_FONTCHANGE, 0,0);
		//Postmessage is low priority
		 SendNotifyMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
	}
	return bOK;
}

void ReportSystemError ( DWORD dwError)
{ 
    LPTSTR   message; 
 
    FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
                   FORMAT_MESSAGE_FROM_SYSTEM, 
                   NULL, 
                   dwError, 
                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
                   (LPTSTR) &message, 
                   0, 
                   NULL); 
 
	HWND hWnd = AfxGetMainWnd()->m_hWnd;
	if (g_pOSIWindow && g_pOSIWindow->m_hWnd)
		hWnd =  g_pOSIWindow->m_hWnd;    
    
    MessageBox(hWnd, message, _T("Ohio Scientific"), MB_OK); 
    LocalFree (message); 

}

/////////////////////////////////////////////////////////////////////////////

unsigned char key_pressed[8] = {0}; //hold current col data for each row of keyboard
unsigned char keyscan = 0;     //Write (Scan)
unsigned char keyptr = 0;     // ptr to RAMptr[0xdf01]      Read (Key)

static int bitmask[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };


COSIWindow* g_pOSIWindow = NULL;
BOOL COSIWindow::m_bPromptRead = 1;
BOOL COSIWindow::m_bPromptWrite = 1;
BOOL COSIWindow::m_bBASIC = 1;
int COSIWindow::m_nEmulation = 0;

void COSIWindow::ClearKeyScan(BOOL bNoCaps /*=FALSE*/)
{

	for (int i = 0; i < 8; i++)
	{
		key_pressed[i] = 0;
	}
	
	if (!bNoCaps)
		key_pressed[0] = 1; //SHIFT LOCK
}

void COSIWindow::CharToKeystate(unsigned char c, BOOL bShiftOnly /*=FALSE*/, BOOL bC1P /*= FALSE*/)
{
#define CAPS (0x10000)
#define SHIFT (0x20000)
#define CTRL (0x40000)

	static int keys[128];
	static BOOL bKeyInit = FALSE;
	if (c > 127)
		return;
	COutputMem& mem= *m_pMem;
		
	if (isprint(c))
		TRACE( _T("CharToKeystate %c %s\r\n"), c, bShiftOnly ? _T("SO") : _T(""));
	else
		TRACE( _T("CharToKeystate <%02x> %s\r\n"), c, bShiftOnly ? _T("SO") : _T(""));
	if (!bKeyInit)
	{
		bKeyInit = TRUE;

		//table to convert ascii char to keyboard scan position
		//not all values are supported since the OSI keypoller 
		//doesn't generate all values;
		//HiByte is row, Lowbyte is column stored in C4P values
		keys[0x00] = 0x0202 + CAPS + SHIFT + CTRL;
		keys[0x01] = 0x0240 + CAPS + CTRL;
		keys[0x02] = 0x0410 + CAPS + CTRL;
		keys[0x03] = 0x0440 + CAPS + CTRL;
		keys[0x04] = 0x0840 + CAPS + CTRL;
		keys[0x05] = 0x1040 + CAPS + CTRL;
		keys[0x06] = 0x0820 + CAPS + CTRL;
		keys[0x07] = 0x0810 + CAPS + CTRL;
		keys[0x08] = 0x0808 + CAPS + CTRL;
		keys[0x09] = 0x1002 + CAPS + CTRL;
		keys[0x0a] = 0x2010 + CAPS;//0x0804 + CAPS + CTRL;	 //LF
		keys[0x0b] = 0x0802 + CAPS + CTRL;
		keys[0x0c] = 0x2040 + CAPS + CTRL;
		keys[0x0d] = 0x2008 + CAPS;//0x0404 + CAPS + CTRL;  //CR
		keys[0x0e] = 0x0408 + CAPS + CTRL;
		keys[0x0f] = 0x2020 + CAPS + CTRL;	//^O

		keys[0x10] = 0x0202 + CAPS + CTRL;
		keys[0x11] = 0x0280 + CAPS + CTRL;
		keys[0x12] = 0x1020 + CAPS + CTRL;
		keys[0x13] = 0x0880 + CAPS + CTRL;
		keys[0x14] = 0x1010 + CAPS + CTRL;
		keys[0x15] = 0x1004 + CAPS + CTRL;
		keys[0x16] = 0x0420 + CAPS + CTRL;
		keys[0x17] = 0x1080 + CAPS + CTRL;
		keys[0x18] = 0x0480 + CAPS + CTRL;
		keys[0x19] = 0x1008 + CAPS + CTRL;
		keys[0x1a] = 0x0220 + CAPS + CTRL;
		keys[0x1b] = 0x0120 + CAPS;			//esc
		keys[0x1c] = 0x2040 + CAPS + SHIFT + CTRL;
		keys[0x1d] = 0x0404 + CAPS + SHIFT + CTRL;
		keys[0x1e] = 0x0408 + CAPS + SHIFT + CTRL;
		keys[0x1f] = 0x2020 + CAPS + SHIFT + CTRL;

		keys[0x20] = 0x0210 + CAPS;			//' '
		keys[0x21] = 0x8080 + CAPS + SHIFT;
		keys[0x22] = 0x8040 + CAPS + SHIFT;
		keys[0x23] = 0x8020 + CAPS + SHIFT;
		keys[0x24] = 0x8010 + CAPS + SHIFT;
		keys[0x25] = 0x8008 + CAPS + SHIFT;
		keys[0x26] = 0x8004 + CAPS + SHIFT;
		keys[0x27] = 0x8002 + CAPS + SHIFT;
		keys[0x28] = 0x4080 + CAPS + SHIFT;
		keys[0x29] = 0x4040 + CAPS + SHIFT;
		keys[0x2a] = 0x4010 + CAPS + SHIFT;
		keys[0x2b] = 0x0204 + CAPS + SHIFT; //+
		keys[0x2c] = 0x0402 + CAPS;
		keys[0x2d] = 0x4008 + CAPS;
		keys[0x2e] = 0x2080 + CAPS;
		keys[0x2f] = 0x0208 + CAPS;

		keys[0x30] = 0x4020 + CAPS;	//0
		keys[0x31] = 0x8080 + CAPS;
		keys[0x32] = 0x8040 + CAPS;
		keys[0x33] = 0x8020 + CAPS;
		keys[0x34] = 0x8010 + CAPS;
		keys[0x35] = 0x8008 + CAPS;
		keys[0x36] = 0x8004 + CAPS;
		keys[0x37] = 0x8002 + CAPS;
		keys[0x38] = 0x4080 + CAPS;
		keys[0x39] = 0x4040 + CAPS;
		keys[0x3a] = 0x4010 + CAPS;
		keys[0x3b] = 0x0204 + CAPS;
		keys[0x3c] = 0x0402 + CAPS + SHIFT;
		keys[0x3d] = 0x4008 + CAPS + SHIFT;
		keys[0x3e] = 0x2080 + CAPS + SHIFT;
		keys[0x3f] = 0x0208 + CAPS + SHIFT;

		keys[0x40] = 0x4020 + CAPS + SHIFT;	//@	 cegmon - 0x4020	was 0x0202 + CAPS + SHIFT;
		keys[0x41] = 0x0240 + CAPS;	// A
		keys[0x42] = 0x0410 + CAPS;
		keys[0x43] = 0x0440 + CAPS;
		keys[0x44] = 0x0840 + CAPS;
		keys[0x45] = 0x1040 + CAPS;
		keys[0x46] = 0x0820 + CAPS;
		keys[0x47] = 0x0810 + CAPS;
		keys[0x48] = 0x0808 + CAPS;
		keys[0x49] = 0x1002 + CAPS;
		keys[0x4a] = 0x0804 + CAPS;
		keys[0x4b] = 0x0802 + CAPS;
		keys[0x4c] = 0x2040 + CAPS;
		keys[0x4d] = 0x0404 + CAPS;
		keys[0x4e] = 0x0408 + CAPS;
		keys[0x4f] = 0x2020 + CAPS;

		keys[0x50] = 0x0202 + CAPS;
		keys[0x51] = 0x0280 + CAPS;
		keys[0x52] = 0x1020 + CAPS;
		keys[0x53] = 0x0880 + CAPS;
		keys[0x54] = 0x1010 + CAPS;
		keys[0x55] = 0x1004 + CAPS;
		keys[0x56] = 0x0420 + CAPS;
		keys[0x57] = 0x1080 + CAPS;
		keys[0x58] = 0x0480 + CAPS;
		keys[0x59] = 0x1008 + CAPS;
		keys[0x5a] = 0x0220 + CAPS;	// Z
		keys[0x5b] = 0x0802 + CAPS + SHIFT;
		keys[0x5c] = 0x2040 + CAPS + SHIFT;
		keys[0x5d] = 0x0404 + CAPS + SHIFT;
		keys[0x5e] = 0x0408 + CAPS + SHIFT;	 //^ SHIFT-N
		keys[0x5f] = 0x2020 + CAPS + SHIFT;	 //_

		keys[0x60] = 0; //`
		keys[0x61] = 0x0240; //a
		keys[0x62] = 0x0410;
		keys[0x63] = 0x0440;
		keys[0x64] = 0x0840;
		keys[0x65] = 0x1040;
		keys[0x66] = 0x0820;
		keys[0x67] = 0x0810;
		keys[0x68] = 0x0808;
		keys[0x69] = 0x1002;
		keys[0x6a] = 0x0804;
		keys[0x6b] = 0x0802;
		keys[0x6c] = 0x2040;
		keys[0x6d] = 0x0404;
		keys[0x6e] = 0x0408;
		keys[0x6f] = 0x2020;

		keys[0x70] = 0x0202; 
		keys[0x71] = 0x0280; 
		keys[0x72] = 0x1020; 
		keys[0x73] = 0x0880; 
		keys[0x74] = 0x1010; 
		keys[0x75] = 0x1004; 
		keys[0x76] = 0x0420; 
		keys[0x77] = 0x1080; 
		keys[0x78] = 0x0480; 
		keys[0x79] = 0x1008; 
		keys[0x7a] = 0x0220; // z
		keys[0x7b] = 0; 	// {
		keys[0x7c] = 0; 	// |
		keys[0x7d] = 0; 	// }
		keys[0x7e] = 0; 	// ~
		keys[0x7f] = 0x4004 + CAPS; //rub 
	}
	int i;
	int nVal = keys[c];
	if (bShiftOnly)
	{
		nVal &= (CAPS | SHIFT | CTRL);
	}


	int row = (nVal >> 8) & 0xFF;
	int col = nVal & 0xFF;
	
	ClearKeyScan(TRUE);
	
	if (nVal & SHIFT)
	{
		SHORT wState;
		BOOL bShift = FALSE;
		wState = GetAsyncKeyState(VK_RSHIFT);
		key_pressed[0] &= ~0x06;
		
		if (wState & 0x8000)
		{
			bShift = TRUE;
			key_pressed[0] |= 0x02;
		}
	
		wState = GetAsyncKeyState(VK_LSHIFT);
		if (wState & 0x8000)
		{
			bShift = TRUE;
			key_pressed[0] |= 0x04;
		}
			
		if (!bShift)
			key_pressed[0] |= 0x04;		
		
	}

	//handle modifier keys
	if (nVal & CAPS)
		key_pressed[0] |= 0x01;
	else
		key_pressed[0] &= 0xFE;	//not pressed

	if (nVal & CTRL)
		key_pressed[0] |= 0x40;
	else
		key_pressed[0] &= 0xBF;


	TRACE(_T("simukey = %c\r\n"), c);
	
	//	scan the active rows on the keyboard matrix and return the columns with keys pressed
	c =0x00;

	for (i = 0; i < 8; i++)
	{
		if (bitmask[i] & row)
			key_pressed[i] |= (col);
			
		if ( (bitmask[i] & keyscan) !=0)
			c |= key_pressed[i];			
	}	

	m_nKeyTick = 200000;

	//Invert result for C2/C4
	if (bC1P)
		c = (BYTE) (c ^ 0xFF);
	else
		c = (BYTE) c;
		
	if (keyptr != c)
	{
		keyptr = c;
		mem[0xDf00] = c;
		SendMessageToViews(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)0xdf00);
		SendMessageToPopups(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)0xdf00);
	}

}

/////////////////////////////////////////////////////////////////////////////
// COSIWindow

bool COSIWindow::m_bRegistered= FALSE;
bool COSIWindow::m_bHidden;
CString COSIWindow::m_strClass;
CFont COSIWindow::m_OSIFont;
HANDLE COSIWindow::m_hOSITTF;
HANDLE COSIWindow::m_hUK101TTF;
static const TCHAR ENTRY_OSI[]= _T("OSISim");
static const TCHAR ENTRY_WINDOW[]= _T("Window");
static const TCHAR LAST_PATH[] = _T("LastPath");
static const TCHAR PROMPTREAD[] = _T("PromptRead");
static const TCHAR PROMPTWRITE[] = _T("PromptWrite");
static const TCHAR BASIC[] = _T("BASIC");
static const TCHAR Emulation[] = _T("Emulation");

static const TCHAR szWinFormat[] = _T("%u,%u,%d,%d,%d,%d,%d,%d,%d,%d");

LOGFONT COSIWindow::m_LogFont=
{
  -12,	// LONG lfHeight;
  0,	// LONG lfWidth;
  0,	// LONG lfEscapement;
  0,	// LONG lfOrientation;
  FW_DONTCARE,	// LONG lfWeight;
  0,	// BYTE lfItalic;
  0,	// BYTE lfUnderline;
  0,	// BYTE lfStrikeOut;
  DEFAULT_CHARSET,	// BYTE lfCharSet;
  OUT_DEFAULT_PRECIS,	// BYTE lfOutPrecision;
  CLIP_DEFAULT_PRECIS,	// BYTE lfClipPrecision;
  NONANTIALIASED_QUALITY,	// BYTE lfQuality;
  FIXED_PITCH | FF_DONTCARE | TMPF_TRUETYPE,	// BYTE lfPitchAndFamily;
  _T("OSI")	// CHAR lfFaceName[LF_FACESIZE];
};

CPoint COSIWindow::m_WndPos= CPoint(0,0);	// położenie okna
int COSIWindow::m_nInitW= 64;
int COSIWindow::m_nInitH= 32;
COLORREF COSIWindow::m_rgbTextColor= RGB(255,255,255);
COLORREF COSIWindow::m_rgbBackgndColor= RGB(0,0,0);

IMPLEMENT_DYNCREATE(COSIWindow, CMiniFrameWnd)

BEGIN_MESSAGE_MAP(COSIWindow, CMiniFrameWnd)
  //{{AFX_MSG_MAP(COSIWindow)
	ON_WM_PAINT()
	ON_WM_GETMINMAXINFO()
	ON_WM_SIZE()
	ON_WM_DESTROY()
	ON_WM_CHAR()
	ON_WM_CLOSE()
	ON_WM_KEYDOWN()
	ON_WM_CONTEXTMENU()
	ON_COMMAND(ID_PASTE, OnPaste)
    ON_WM_SIZING()
	//}}AFX_MSG_MAP
    ON_MESSAGE(CBroadcast::WM_USER_START_DEBUGGER, OnStartDebug)
    ON_MESSAGE(CBroadcast::WM_USER_EXIT_DEBUGGER, OnExitDebug)
    ON_MESSAGE(CBroadcast::WM_USER_MEM_CHANGED, OnMemChanged)
END_MESSAGE_MAP()

void COSIWindow::RegisterWndClass()
{
  ASSERT(!m_bRegistered);
  if (m_bRegistered)
    return;
  m_strClass = AfxRegisterWndClass(CS_VREDRAW|CS_HREDRAW|CS_DBLCLKS,
    ::LoadCursor(NULL,IDC_ARROW), (HBRUSH) ::GetStockObject(BLACK_BRUSH), AfxGetApp()->LoadIcon(IDI_OSI_WINDOW));
  m_bRegistered = TRUE;
}
COSIWindow::COSIWindow()
{
	m_hWnd = 0;

	if (!m_bRegistered)
		RegisterWndClass();

	m_bHidden = FALSE;
    m_nRows = 32;
    m_nCols = 64;
    m_bDoubleHalf = false;
    m_bDoubleHeight = false;
    m_nKeyTick = 0;
    m_nEmulation = 0;
    m_bC1P64x16 = 0;
    m_nVidTick = 0;
    
    m_pMem = theApp.m_global.GetMem();
   ZeroMemory(&m_FontSizes,sizeof(m_FontSizes));
}

COSIWindow::~COSIWindow()
{

}

//-----------------------------------------------------------------------------
// Nowe okno

bool COSIWindow::Create()
{
	g_pOSIWindow = this;
	if (m_nEmulation < 0 || m_nEmulation > 2)
		m_nEmulation = 0;
	
	ASSERT(m_hWnd==0);
	CString title;
	title=_T("Ohio Scientific");
	
	RECT rect= {20,20,100,100};
	rect.left = m_WndPos.x;
	rect.top = m_WndPos.y;
	WINDOWPLACEMENT wp = {0};
	
	CString strBuffer = AfxGetApp()->GetProfileString(ENTRY_OSI, ENTRY_WINDOW);
	if (!strBuffer.IsEmpty())
	{
		
		int nRead = _stscanf (strBuffer, szWinFormat,
			&wp.flags, &wp.showCmd,
			&wp.ptMinPosition.x, &wp.ptMinPosition.y,
			&wp.ptMaxPosition.x, &wp.ptMaxPosition.y,
			&wp.rcNormalPosition.left, &wp.rcNormalPosition.top,
			&wp.rcNormalPosition.right, &wp.rcNormalPosition.bottom);

		if (nRead == 10)
		{
			//ensure sane values
			if (RectWidth(wp.rcNormalPosition) > 50 && 
			    RectHeight(wp.rcNormalPosition) > 50 )
			{
				wp.length = sizeof wp;
				rect = wp.rcNormalPosition;
				
			}
		}
	}	
	
	


	if (!CMiniFrameWnd::CreateEx(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE , m_strClass, title,
		WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME/*| MFS_MOVEFRAME | MFS_SYNCACTIVE*/,
		rect, AfxGetMainWnd(), 0))
		return FALSE;
	ModifyStyle(0, WS_THICKFRAME,0);
	ModifyStyleEx(0,WS_EX_WINDOWEDGE|WS_EX_CLIENTEDGE);
	if (wp.length)
	{
		PostMessage(WM_SIZE,SIZE_RESTORED,MAKELPARAM(RectWidth(wp.rcNormalPosition),RectHeight(wp.rcNormalPosition)) );
	}
	 
	if (!m_hOSITTF)
		m_hOSITTF = LoadFont(IDR_OSIFONT, _T("OSI") );
	if (!m_hUK101TTF)
		m_hUK101TTF = LoadFont(IDR_UK101FONT, _T("UK101") );


	
	if ( m_hOSITTF || IsFontAvailable(m_nEmulation != 2 ? _T("OSI") : _T("UK101"), NULL) )
	{
		ZeroMemory(&m_LogFont,sizeof(LOGFONT));
		
		int nPos = 0;
		TEXTMETRIC tm;
		CWnd* pWnd = AfxGetMainWnd();
		CDC* pDC = NULL;
		if (pWnd)
			pDC= pWnd->GetDC(); 

		if (!pDC)
			return FALSE;
		if (m_OSIFont.m_hObject)
		{
			DeleteObject(m_OSIFont.m_hObject);
			m_OSIFont.m_hObject = NULL;
		}
		m_LogFont.lfHeight =         -12 ;                    
		m_LogFont.lfWidth =          0 ;                     
		m_LogFont.lfEscapement =     0 ;                     
		m_LogFont.lfOrientation =    0 ;                     
		m_LogFont.lfWeight =         FW_DONTCARE ;                     
		m_LogFont.lfItalic =         0 ;                     
		m_LogFont.lfUnderline =      0 ;                     
		m_LogFont.lfStrikeOut =      0 ;                     
		m_LogFont.lfCharSet =        DEFAULT_CHARSET ;           
		m_LogFont.lfOutPrecision =   OUT_DEFAULT_PRECIS ;    
		m_LogFont.lfClipPrecision =  CLIP_DEFAULT_PRECIS;    
		m_LogFont.lfQuality =        NONANTIALIASED_QUALITY ;       
		m_LogFont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE | TMPF_TRUETYPE;
		//_tcsncpy(m_LogFont.lfFaceName, m_nEmulation != 2 ? _T("OSI") : _T("UK101"), LF_FACESIZE);
		_tcsncpy(m_LogFont.lfFaceName, _T("OSI"), LF_FACESIZE);
		m_OSIFont.CreateFontIndirect(&m_LogFont);
				
			
		for (int i = 1; i < MAX_FONT_SIZES; i++)
		{
			m_LogFont.lfHeight = -i ;             


			HFONT hFont = ::CreateFontIndirect(&m_LogFont);
			HFONT hOldFont = (HFONT) SelectObject( pDC->m_hDC, hFont ) ;
			pDC->GetTextMetrics( &tm ) ;
			SelectObject( pDC->m_hDC, hOldFont ) ;
			DeleteObject(hFont);

			//
			// character width and height
			//
			//Both internal fonts have the same sizes
			if (nPos == 0 || m_FontSizes[nPos-1].cellWidth != tm.tmAveCharWidth)
			{
				m_FontSizes[nPos].lfHeight = -i;
				m_FontSizes[nPos].cellWidth = tm.tmAveCharWidth  ;
				m_FontSizes[nPos].cellHeight = tm.tmHeight + tm.tmExternalLeading ;

				nPos++;
				m_FontSizes[nPos].lfHeight = 0;
			}

		}
		ReleaseDC( pDC ) ;
	
	}
	ChangeSettings();
  	
    SetFocus();
	return TRUE;
}


void COSIWindow::CalcFontSize()		// obliczenie rozmiaru znaków
{
  ASSERT(m_hWnd);
  CClientDC dc(this);
  dc.SelectObject(&m_OSIFont);
  TEXTMETRIC tm;
  dc.GetTextMetrics(&tm);
  cellHeight = (int)tm.tmHeight + (int)tm.tmExternalLeading;
  cellWidth = (int)tm.tmAveCharWidth;
}


void COSIWindow::ChangeSettings()
{
	m_pMem = theApp.m_global.GetMem();
	if (!m_pMem)
		return;
	COutputMem& mem= *m_pMem;
	int oldCols = m_nCols;
	int oldRows = m_nRows;
	
	switch (m_nEmulation)
	{
		case 0: //C2P
			m_nRows = 32;
			m_nCols = 64;
			m_bDoubleHeight = false;
			m_bDoubleHalf = false;
			SetWindowText(_T("Ohio Scientific C2P"));
			break;
		case 1:	//C1P
			m_nRows = 32;
			m_nCols = 32;
			m_bDoubleHeight = false;
			m_bDoubleHalf = false;		
			SetWindowText(_T("Ohio Scientific C1P"));
		    if (m_bC1P64x16)
		    {
				m_nRows = 16;
				m_nCols = 64;				
				m_bDoubleHeight = true;
		    }

			break;
		case 2: //UK101
			m_nRows = 16;
			m_nCols = 64;
			m_bDoubleHeight = true;
			m_bDoubleHalf = false;			
			SetWindowText(_T("Compukit UK101"));
			break;
		
	}

	LoadROMS();

    if (m_OSIFont.m_hObject)
    {
        DeleteObject(m_OSIFont.m_hObject);
		m_OSIFont.m_hObject = NULL;
    }
    _tcsncpy(m_LogFont.lfFaceName, m_nEmulation != 2 ? _T("OSI") : _T("UK101"), LF_FACESIZE);
    
    int mul = m_bDoubleHeight ? 2 : 1;
	if (oldRows != m_nRows || oldCols != m_nCols)
	{
		BOOL bConstrainWidth = false, bConstrainHeight = false;
		if (oldRows != m_nRows)
		{
			bConstrainHeight = true;
			bConstrainWidth = false;		
		}
		else
		{
			bConstrainHeight = false;
			bConstrainWidth = true;		
		}
		CRect  rWin, rClient;
		GetWindowRect( &rWin);
		GetClientRect( &rClient);
		int xDiff = rWin.Width() - rClient.Width();
		int yDiff = rWin.Height() - rClient.Height();
		//find font that fits in target client
		int nSize = 0;
		for (int i = 0; i < MAX_FONT_SIZES; i++)
		{
			if (m_FontSizes[i].lfHeight == 0)
				break;

		if ( (m_FontSizes[i].cellWidth * m_nCols > rWin.Width() && bConstrainWidth) ||
			 (m_FontSizes[i].cellHeight * m_nRows * mul  > rWin.Height() && bConstrainHeight) )
			break;

			nSize = i;
		}
		int width = m_FontSizes[nSize].cellWidth * m_nCols;// + xDiff;
		int height = m_FontSizes[nSize].cellHeight * m_nRows*mul;// + (yDiff); 
		
		//if (m_FontSizes[nSize].lfHeight != m_LogFont.lfHeight)
		{
			m_LogFont.lfHeight = m_FontSizes[nSize].lfHeight;
			InitNewFont( m_LogFont);
		}
		SetWindowPos(NULL,rWin.left,rWin.top,width,height,0);		
		//GetParent()->SendMessage(WM_SIZE,SIZE_RESTORED,MAKELPARAM(width+xDiff, height+yDiff) ); 
		//OnSize(SIZE_RESTORED, width+xDiff, height+yDiff);
		Invalidate(TRUE);
	}    

}

void COSIWindow::LoadROMS()
{
	m_pMem = theApp.m_global.GetMem();
	if (!m_pMem)
		return;
	
	COutputMem& mem= *m_pMem;	
	if (m_nEmulation == 0 || m_nEmulation == 1)
	{
		unsigned char buf[2048];
		int nSize = load_resource(_T("ROM"), IDR_ROM_SYN600 , buf, 2048);
		if (m_nEmulation == 0)
		{
			memcpy(	&mem[0xfd00], buf+0x100, 256);
			memcpy(	&mem[0xfe00], buf+0x200, 256);
			memcpy(	&mem[0xff00], buf+0x300, 256);
		}
		else
		{
			memcpy(	&mem[0xfc00], buf+0x400,  1024);
		}
		SendMessageToViews(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)0xfc00, (LPARAM)0xFFFF);
		SendMessageToPopups(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)0xfc00, (LPARAM)0xFFFF);		
	}
	else
	{
		int nSize = load_resource(_T("ROM"), IDR_ROM_NEWMON16 , &mem[0xf800], 2048);
		SendMessageToViews(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)0xf800, (LPARAM)0xFFFF);
		SendMessageToPopups(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)0xf800, (LPARAM)0xFFFF);			
	}
	
	if (m_bBASIC)
	{
		int nSize = load_resource(_T("ROM"), (m_nEmulation == UK101) ? IDR_ROM_BASICUK : IDR_ROM_BASIC , &mem[0xA000], 8192);
		SendMessageToViews(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)0xa000, (LPARAM)0xBFFF);
		SendMessageToPopups(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)0xa000, (LPARAM)0xBFFF);	
	}
}

  // ustawienie położenia okna
void COSIWindow::SetWndPos(const POINT &p)
{
  m_WndPos = p;
  if (m_hWnd==0)	// nie ma jeszcze okna?
    return;
  SetWindowPos(NULL,p.x,p.y,0,0,SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
}

  // odczyt położenia okna
CPoint COSIWindow::GetWndPos()
{
  if (m_hWnd==0)	// nie ma jeszcze okna?
    return m_WndPos;
  CRect rect;
  GetWindowRect(rect);
  return rect.TopLeft();
}

unsigned char COSIWindow::Read(int addr)
{
	int i;
	unsigned char c = 0;
	COutputMem& mem= *m_pMem;
	if (addr >= 0xD000 && addr <= 0xD7FF)
		return mem[addr];
	if (m_nEmulation == C2P && addr >= 0xDE00 && addr <= 0xDEFF)
	{
		c = 0xDF;
		if (m_nVidTick % 33333 < 16666)
			c = 0x5F;
			
		return c;
	}
	
	//C1P KB occupies DC00-DFFF
	if ( (addr >= 0xDF00 && addr <= 0xDFFF) || (m_nEmulation != C2P && addr >= 0xDC00 && addr <= 0xDFFF) )
	{
		if ((addr & 1) == 0)
		{
			//read KB col
			//	scan the active rows on the keyboard matrix and return the columns with keys pressed
			for (i = 0; i < 8; i++)
			{
				if ( (bitmask[i] & keyscan) !=0)
					c |= key_pressed[i];
			}
			if (!c && (keyscan&0x01) == 1)
				c = 1;
			//Invert result for C2/C4
			if (m_nEmulation != C2P)
				c = (BYTE) (c ^ 0xFF);
			else
				c = (BYTE) c;
				
			if (keyptr != c)
			{
				keyptr = c;
				mem[0xdf00] = c;
				SendMessageToViews(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)0xdf00);
				SendMessageToPopups(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)0xdf00);
			}
	
		}
		else
		{
			c = (addr >> 8)&0xFF;
		}
	}
	else if ( ((m_nEmulation != C2P) && (addr >= 0xF000 && addr <= 0xF0FF)) || ((m_nEmulation == C2P) && (addr >= 0xFC00 && addr <= 0xFCFF)))
	{
		m_Serial.Read(addr, &c);
		mem[addr & 0xFF01] = c;  //mirror bits in "RAM"
	}
	else
	{
		return mem[addr];
	}
	return c;
}
void COSIWindow::Write(int addr, unsigned char data)
{
	COutputMem& mem= *m_pMem;
	if (addr >= 0xD000 && addr <= 0xD7FF)
	{
		mem[addr] = data;
		Invalidate(false);	
		//SendMessageToViews(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)addr, (LPARAM) addr);
		//SendMessageToPopups(CBroadcast::WM_USER_MEM_CHANGED,(WPARAM)addr,(LPARAM) addr); 		
		return;
	}
	if ( (addr >= 0xDF00 && addr <= 0xDFFF) || (m_nEmulation != C2P && addr >= 0xDC00 && addr <= 0xDFFF))   //keyboard
	{
		if (addr & 1)
		{
			//nothing  yet
		}
		else
		{	//write KB row
			if (m_nEmulation != C2P)
				keyscan = data ^ 0xFF;	 //invert KB for C1P/UK101
			else
				keyscan = data;
		}
		return;
	}

	if (m_nEmulation == C2P)
	{	//C2P specific
		if (addr >= 0xFC00 && addr <= 0xFCFF) //C2P serial port
			m_Serial.Write(addr, data);
		if (addr >= 0xDE00 && addr <= 0xDEFF)  //C2P video width, color & sound ctrl
		{
			//toggle screen width?
			BOOL bWide = !(data & 0x01);
			if (bWide != m_bDoubleHalf )
			{
				m_bDoubleHalf = bWide;
				Invalidate();
			}
		}
	}
	else
	{	
		if (addr >= 0xF000 && addr <= 0xF0FF) //C1P serial port
			m_Serial.Write(addr, data);
		else if (m_nEmulation == C1P && addr >= 0xD800 && addr <= 0xDBFF) //OSI600D 32x32 vs 16x64
		{
			//TODO
			BOOL bWide = (data & 0x01) != 0;
			if (bWide != m_bC1P64x16)
			{
				m_bC1P64x16 = bWide;
				ChangeSettings();
			}
		}
	}
	if (m_bBASIC && addr >= 0xA000 && addr <= 0xBFFF)
		return;	
}

BOOL COSIWindow::IsIOAddr(int addr, BOOL bWrite /*= FALSE*/ )
{
	//keyboard
	if (addr >= 0xDF00 && addr <= 0xDFFF)
		return TRUE;
	if ( m_nEmulation == C2P)
	{	//C2P specific
		if (addr >= 0xD000 && addr <0xD800)
			return TRUE;	
		if (addr >= 0xFC00 && addr <= 0xFCFF)
			return TRUE;
		if (addr >= 0xDE00 && addr <= 0xDEFF)
			return TRUE;
		if (addr >= 0xFD00 && addr <= 0xFFFF)
			return TRUE;
	}
	else
	{	//C1P / UK101 specific
		if (addr >= 0xD000 && addr <0xD400)
			return TRUE;
		if (m_nEmulation == C1P && addr >= 0xD800 && addr <= 0xDFFF)
			return TRUE;					
		if (addr >= 0xF000 && addr <= 0xF0FF)
			return TRUE;
		if (addr >= 0xF800 && addr <= 0xFFFF)
			return TRUE;			
	}
	if (m_bBASIC)
	{
		if (addr >= 0xA000 && addr <0xC000)
			return TRUE;
	}
	return FALSE;
}
void COSIWindow::Tick(int clocks)
{
	m_nVidTick+=clocks;
	
	if (m_nKeyTick > 0)
	{
		m_nKeyTick -= min(m_nKeyTick, clocks);
		if (m_nKeyTick == 0)
		{
			ClearKeyScan();
			m_nKeyTick = -6000;   //key UP time
		}
	}
	else if (m_nKeyTick < 0)
	{
		m_nKeyTick += clocks;
		if ( m_nKeyTick > 0)
			m_nKeyTick = 0;
	}
	else
	{
		if ( !m_PasteBuf.IsEmpty())
		{
			unsigned char c = m_PasteBuf[0];
			m_PasteBuf = m_PasteBuf.Mid(1);
			CharToKeystate(c, 0, m_nEmulation > 0);
			m_nKeyTick = 100000;
		}	
	}
	m_Serial.clock_tick(clocks);

}

/////////////////////////////////////////////////////////////////////////////
// COSIWindow message handlers

void COSIWindow::OnPaint()
{
  CPaintDC dc(this);	// device context for painting
  WCHAR wLine[80] = {0};
    
	if (!m_pMem)
		m_pMem = theApp.m_global.GetMem();
	if (!m_pMem)
		return;
  const COutputMem& mem= *m_pMem;
  CRect rect;
  GetClientRect (&rect);
    
  CDC mDC;
  mDC.CreateCompatibleDC(&dc);
  CBitmap bitmap;
  bitmap.CreateBitmap(rect.Width(), rect.Height(),1,1,NULL);
  mDC.SelectObject(&m_OSIFont);
  mDC.SetBkMode(OPAQUE);
  mDC.SetTextColor(m_rgbTextColor);
  mDC.SetBkColor(m_rgbBackgndColor);  
  CBitmap* pOldBM = (CBitmap*) mDC.SelectObject(&bitmap);
  
  CString line;
  for (int row = 0,y=0; row < m_nRows ; row++, y+=cellHeight)
  {
	int addr = 0xD000;
	
	for (int col = 0; col < m_nCols; col++)
	{
		WCHAR wChar = (unsigned char)mem[addr+row*m_nCols+col];

		if (0)
		{
			if (wChar == 0x7c)
				wChar = 0x7D;
			else if (wChar == 0x7D)
				wChar = 0x7C;
		}
		wChar |= 0xF000;  //mapping to symbol range in font 0-255
	
		wLine[col] = wChar;
	
	}
	::TextOutW(mDC.m_hDC,2, y+1 , wLine, m_nCols);
  }
  if (!m_bDoubleHeight)
	dc.StretchBlt(0,0,rect.Width(),rect.Height(), &mDC,0,0,m_bDoubleHalf ? rect.Width()/2 : rect.Width(), rect.Height(),SRCCOPY);
  else
	dc.StretchBlt(0,0,rect.Width(),rect.Height(), &mDC,0,0, rect.Width(), rect.Height()/2,SRCCOPY);
  mDC.SelectObject(pOldBM);
  
}

LRESULT COSIWindow::OnMemChanged(WPARAM wParam, LPARAM lParam)
{
	if ( wParam >= 0xD000 && wParam <= 0xD7FF)
	{
		 if (m_hWnd)
			Invalidate(false);
	}
	return 0;
}

//=============================================================================

void COSIWindow::OnDestroy()
{
	CRect rect;
	GetWindowRect(&rect);
	m_WndPos = rect.TopLeft();	// zapamiętanie położenia okna
	WINDOWPLACEMENT wp;
	wp.length = sizeof wp;

	if (GetWindowPlacement(&wp))
	{
		TCHAR szBuffer[256];
		wp.flags = 0;
		wp.rcNormalPosition;
		if (IsZoomed())
			wp.flags |= WPF_RESTORETOMAXIMIZED;

		wsprintf (szBuffer, szWinFormat,
		wp.flags, wp.showCmd,
		wp.ptMinPosition.x, wp.ptMinPosition.y,
		wp.ptMaxPosition.x, wp.ptMaxPosition.y,
		wp.rcNormalPosition.left, wp.rcNormalPosition.top,
		wp.rcNormalPosition.right, wp.rcNormalPosition.bottom);

		AfxGetApp()->WriteProfileString(ENTRY_OSI, ENTRY_WINDOW, szBuffer);			
	}  

  CMiniFrameWnd::OnDestroy();
  g_pOSIWindow = NULL;
}

//=============================================================================

void COSIWindow::PostNcDestroy()
{
//	CMiniFrameWnd::PostNcDestroy();
  m_hWnd = NULL;
}

//=============================================================================

void COSIWindow::OnGetMinMaxInfo(MINMAXINFO* pMMI)
{
  CMiniFrameWnd::OnGetMinMaxInfo(pMMI);
}


//-----------------------------------------------------------------------------

void COSIWindow::SetColors(COLORREF text, COLORREF backgnd)	// ust. kolorów
{
  m_rgbBackgndColor = backgnd;
  m_rgbTextColor = text;
  if (m_hWnd)
    Invalidate();
}

void COSIWindow::GetColors(COLORREF &text, COLORREF &backgnd)	// odczyt kolorów
{
  text = m_rgbTextColor;
  backgnd = m_rgbBackgndColor;
}

//=============================================================================

afx_msg LRESULT COSIWindow::OnStartDebug(WPARAM /*wParam*/, LPARAM /* lParam */)
{
  if (!m_bHidden)
  {	
    if (m_hWnd)
    {
		ShowWindow(SW_NORMAL);
		LoadROMS();	   //debugger zeros memory
    }
    else
		Create();
  }
   
  return 1;
}


afx_msg LRESULT COSIWindow::OnExitDebug(WPARAM /*wParam*/, LPARAM /* lParam */)
{
  if (m_hWnd && (GetStyle() & WS_VISIBLE))	
  {
    m_bHidden = FALSE;
    ShowWindow(SW_HIDE);
  }
  else
    m_bHidden = TRUE;
  return 1;
}

void COSIWindow::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (nChar == 0x1B && !m_PasteBuf.IsEmpty())
	{	//cancel paste
		m_PasteBuf.Empty();
		return;
	}
	
	CharToKeystate(nChar, 0, m_nEmulation > 0);
}

void COSIWindow::OnClose()
{
	ShowWindow(SW_HIDE);
//  CMiniFrameWnd::OnClose();
}


void COSIWindow::Paste()
{
	if (!::IsClipboardFormatAvailable(CF_TEXT))
		return;

	if (!OpenClipboard())
		return;

	if (HANDLE hGlb= ::GetClipboardData(CF_TEXT))
	{
		if (VOID* pStr= ::GlobalLock(hGlb))
		{
			m_PasteBuf = (reinterpret_cast<char*>(pStr));
			GlobalUnlock(hGlb);
		}
	}

	CloseClipboard();
}


void COSIWindow::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (nChar == VK_INSERT)
		Paste();
	else
		CMiniFrameWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}


BOOL COSIWindow::PreTranslateMessage(MSG* pMsg)
{
	if (GetFocus() == this)
	{
		if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP)
		{
			if (pMsg->wParam >= VK_SPACE && pMsg->wParam <= 'Z')
			{
				if (::GetKeyState(VK_CONTROL) < 0 && ::GetKeyState(VK_SHIFT) >= 0)
				{
					// skip the rest of PreTranslateMessage() functions, cause they will
					// eat some of those messages (as accel shortcuts); translate and
					// dispatch them now

					::TranslateMessage(pMsg);
					::DispatchMessage(pMsg);
					return true;
				}
			}
		}
	}

	return CMiniFrameWnd::PreTranslateMessage(pMsg);
}


void COSIWindow::OnContextMenu(CWnd* pWnd, CPoint point)
{
	CMenu menu;
	if (!menu.LoadMenu(IDR_POPUP_TERMINAL))
		return;
	CMenu *pPopup = menu.GetSubMenu(0);
	ASSERT(pPopup != NULL);

	if (point.x == -1 && point.y == -1)		// menu wywołane przy pomocy klawiatury?
	{
		CRect rect;
		GetClientRect(rect);
		ClientToScreen(rect);
		point = rect.CenterPoint();
	}

	pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
}


void COSIWindow::OnPaste()
{
	Paste();
}

void COSIWindow::OnSizing(UINT fwSide, LPRECT lpRect)
{
	CMiniFrameWnd::OnSizing(fwSide, lpRect);
	
	CRect rClient, rWin;
	GetWindowRect( &rWin);
	GetClientRect( &rClient);
	//calc size needed by OS
	int xDiff = rWin.Width() - rClient.Width();
	int yDiff = rWin.Height() - rClient.Height();

	//now given main window size
	//what would the client size be?
	int clientwidth = RectWidth( (*lpRect))-xDiff;
	int clientheight = RectHeight( (*lpRect) )-(yDiff);


	bool bConstrainWidth = true;
	bool bConstrainHeight = true;

	switch (fwSide)
	{

		case WMSZ_RIGHT:
		case WMSZ_LEFT:
			bConstrainHeight = false;
			break;					
		case WMSZ_BOTTOM:
		case WMSZ_TOP:
			bConstrainWidth = false;
			break; 
		 
	}
	//find font that fits in target client
	int nSize = 0;
	int mul = m_bDoubleHeight ? 2 : 1;
	for (int i = 0; i < MAX_FONT_SIZES; i++)
	{
		if (m_FontSizes[i].lfHeight == 0)
			break;

		if ( (m_FontSizes[i].cellWidth * m_nCols > clientwidth && bConstrainWidth) ||
			 (m_FontSizes[i].cellHeight * m_nRows*mul  > clientheight && bConstrainHeight) )
			break;

		nSize = i;
	}
	int width = m_FontSizes[nSize].cellWidth * m_nCols + xDiff+4;
	int height = m_FontSizes[nSize].cellHeight * m_nRows*mul + (yDiff)+2; 

	//now fix asking rect to be font size rect + main window overhead

	//move target rect around based on which corner dragged
	switch (fwSide)
	{
		case WMSZ_TOPRIGHT:
			lpRect->right =	lpRect->left + width;
			lpRect->top = lpRect->bottom - height;
			break;
		case WMSZ_LEFT:
			{
				lpRect->left =	lpRect->right - width;
				int vdiff = RectHeight( (*lpRect) ) - height;
				lpRect->top += vdiff/2;
				lpRect->bottom = lpRect->top + height;
			}
			break;
		case WMSZ_RIGHT:
			{
				lpRect->right =	lpRect->left + width;
				int vdiff = RectHeight( (*lpRect)) - height;
				lpRect->top += vdiff/2;
				lpRect->bottom = lpRect->top + height;
			}
			break;

		case WMSZ_BOTTOMRIGHT:
		default:
			lpRect->right =	lpRect->left + width;
			lpRect->bottom = lpRect->top + height;
			break;

		case WMSZ_BOTTOM:
			{
				lpRect->bottom = lpRect->top + height;
				int hdiff = RectWidth( (*lpRect)) - width;
				lpRect->left += hdiff/2;
				lpRect->right = lpRect->left + width;
			}
			break;

		case WMSZ_BOTTOMLEFT:
			lpRect->left = lpRect->right - width;
			lpRect->bottom = lpRect->top + height;

			break;
		case WMSZ_TOP:
			{
				lpRect->top  = lpRect->bottom - height;
				int hdiff = RectWidth( (*lpRect)) - width;
				lpRect->left += hdiff/2;
				lpRect->right = lpRect->left + width;
			}
			break;

		case WMSZ_TOPLEFT:
			lpRect->left = lpRect->right - width;
			lpRect->top = lpRect->bottom - height;
			break;
	 
	}
}

//=============================================================================
void COSIWindow::InitNewFont(LOGFONT LogFont)
{
    TEXTMETRIC tm;

    //
    // if old one exists, then I should delete it
    //
    if (m_OSIFont.m_hObject)
    {
        DeleteObject(m_OSIFont.m_hObject);
		m_OSIFont.m_hObject = NULL;
    }

    m_OSIFont.CreateFontIndirect(&LogFont);
    

    CDC* pDC = GetDC();
    if (pDC)
    {
		CFont* oldFont = pDC->SelectObject( &m_OSIFont ) ;
		pDC->GetTextMetrics(&tm ) ;
		pDC->SelectObject(oldFont);
		ReleaseDC(pDC);

		//
		// character width and height
		//
		cellWidth = tm.tmAveCharWidth  ;
		cellHeight = tm.tmHeight + tm.tmExternalLeading ;
	}

}

void COSIWindow::OnSize(UINT nType, int cx, int cy) 
{
  CMiniFrameWnd::OnSize(nType,cx,cy);
  if (nType == SIZE_MINIMIZED)
	return;
  int mul = m_bDoubleHeight ? 2 : 1;
  
   	int i = 0;
	int nSize = 0;
	if (m_FontSizes[0].lfHeight == 0)
		return;
	for (i = 0; i < 50; i++)
	{
		if (m_FontSizes[i].lfHeight == 0)
			break;

		if (m_FontSizes[i].cellWidth * m_nCols  > cx ||
	 		m_FontSizes[i].cellHeight * m_nRows*mul > cy )
	  		break;
		nSize = i;
	}
	if (m_FontSizes[nSize+1].cellWidth * m_nCols  == cx &&
	    m_FontSizes[nSize+1].cellHeight * m_nRows*mul < cy + 10)
		nSize++; //fudge it up

	TRACE(_T("SizeTTY pick #%d for %dx%d\r\n"), nSize, cx, cy);    
   
   	if (m_FontSizes[nSize].lfHeight != m_LogFont.lfHeight)
	{
		m_LogFont.lfHeight = m_FontSizes[nSize].lfHeight;
		InitNewFont( m_LogFont);
	}

	int nCurW = m_FontSizes[nSize].cellWidth * m_nCols;
	int nCurH = m_FontSizes[nSize].cellHeight * m_nRows*mul;


	m_yoffset = (nCurH - cy) /2 ;
	m_xoffset = (nCurW- cx) /2 ;


	Invalidate(TRUE);
	return;
}


/////////////////////////////////////////////////////////////////////////////
//	Acia class serial port support
//
Acia::Acia( )
{
	m_hInFile  = INVALID_HANDLE_VALUE;
	m_hOutFile = INVALID_HANDLE_VALUE; 

	m_eSpeed = b2400;

	m_nRTickCount = -1;
	m_nTTickCount = -1;
	
	m_cOut = 0xbf; //random?   
	m_txIRQ = 0;      
	m_rxIRQ = 0;      
	m_txIRQenable = FALSE;
	m_rxIRQenable = FALSE;
	m_bIRQConnected = FALSE;
	m_bIRQSet = FALSE;

	m_bAutoSave = FALSE;
	m_bAutoLoad = TRUE;

	m_dwReadBytes = 0;
	m_cStatus = 0; //was acia_TXEmpty;
	m_bResetting = 1;
	m_eDivideSel	= DivideBy1;
	m_eWordSel		= SevenE2;
	m_eXmitCtrl		= RTSNOIRQ;

}

Acia::~Acia()
{
	
	if (m_hInFile && m_hInFile != INVALID_HANDLE_VALUE)
		CloseHandle(m_hInFile);

	if (m_hOutFile && m_hOutFile != INVALID_HANDLE_VALUE)
		CloseHandle(m_hOutFile);


}

void Acia::Reset()
{
	m_eDivideSel	= DivideBy1;
	m_eWordSel		= SevenE2;
	m_eXmitCtrl		= RTSNOIRQ;
	m_rxIRQenable	= FALSE;
	m_txIRQenable	= FALSE;
	BOOL bClear		= FALSE;
	m_rxIRQ = 0;
	m_txIRQ = 0;
	m_cStatus = 0; //was acia_TXEmpty;
	if (m_bIRQSet)
		ClearACIAIRQ();
	m_bIRQSet = FALSE;
	m_nRTickCount = -1;
	m_nTTickCount = -1;

}

void Acia::Write( unsigned int address, unsigned char wvalue)
{
	if ( (address & 1) )
	{
		data_write(wvalue);
	}
	else
	{
		control_write(wvalue);
	}
}

void Acia::Read( unsigned int address, unsigned char *rvalue)
{
	if ( address & 1)
	{
		*rvalue = data_read();
	}
	else
	{
		*rvalue = control_read();

	}
}

//update ACIA internal state 
// nClocks is number of CPU clock cycles passed
// at 1Mhz, there are 1,000,000 cycles per second or 1 cycle every
//.000001 seconds, or 1 cycle every .001 msec
void Acia::clock_tick(int nClocks)
{
	if (m_nRTickCount > 0)
		m_nRTickCount -= min(m_nRTickCount, nClocks);

	if (m_nTTickCount > 0)
		m_nTTickCount -= min(m_nTTickCount, nClocks);

}

BYTE Acia::control_read()
{
		
	if (m_nRTickCount >= 0)	//receive pacing on
	{
		if (m_nRTickCount )
		{
			//we haven't waited long enough for next character
			//m_cStatus &= ~acia_RXFull;	
		}
		else if ((m_cStatus & acia_RXFull) == 0) //only send next when rx buffer empty
		{
			//there is a new character to receive
			if (m_hInFile != INVALID_HANDLE_VALUE || m_bAutoLoad)
			{
				m_cStatus |= acia_RXFull;
				if (m_rxIRQenable && !m_rxIRQ)
				{
 					if (m_bIRQConnected)
					{
						m_rxIRQ = 2;
						if (!m_bIRQSet)
							SetACIAIRQ();
					}
					else
						m_rxIRQ = 1;
				}
			}
		}
	}
	else
	{
		//we're always ready to read next character
		//if there are any
		if (m_hInFile != INVALID_HANDLE_VALUE || m_bAutoLoad)
		{
			m_cStatus |= acia_RXFull;
			if (m_rxIRQenable && !m_rxIRQ)
			{
				if (m_bIRQConnected)
				{
					m_rxIRQ = 2;
					if (!m_bIRQSet)
						SetACIAIRQ();
				}
				else
					m_rxIRQ = 1;

			}
		
		}
	}



	if (m_nTTickCount >=0)	//transmit pacing on
	{
		
		if (m_nTTickCount )
		{
			//but it hasn't been shifted out yet....
		}
		else
		{
			//if CTS high, TXEmpty inhibited
			if ( !(m_cStatus & acia_CTS))
			{
				//time to receive new character
				m_cStatus |= acia_TXEmpty;

				if (m_txIRQenable)
				{
					if (!m_txIRQ)
					{
						
						if (m_bIRQConnected)
						{
							m_txIRQ = 2;
							if (!m_bIRQSet)
								SetACIAIRQ();
						}
						else
							m_txIRQ = 1;
					}
				}
			}
			//else TRACE(_T("CTS inhibited TXempty\r\n"));
		}
	}
	else
	{
		if ( !m_bResetting && (m_cStatus & acia_TXEmpty) == 0)
		{
			//we're always ready to receive next character
			m_cStatus |= acia_TXEmpty;
			
			if (m_txIRQenable)
			{
				if (!m_txIRQ)
				{
					if (m_bIRQConnected)
					{
						m_txIRQ = 2;
						if (!m_bIRQSet)
							SetACIAIRQ();
					}
					else
					{
						m_txIRQ = 1;

					}
				}
			}
		}
	}

	if (m_txIRQ || m_rxIRQ)
	{
		m_cStatus |= acia_IRQ; 
	}
	else
		m_cStatus &= ~acia_IRQ;

	//if DCD was set, this flag is not cleared until control read and data read

	return m_cStatus;
}

// control write
// DB0 = divide sel 0      00= /1	   10= /64
// DB1 = divide sel 1	   01= /16     11= master reset
// DB2 = word sel 0		   000 = 7e2	011 = 7o1		110 = 8e1
// DB3 = word sel 1		   001 = 7o2	100 = 8n2		111 = 8o1
// DB4 = word sel 2		   010 = 7e1	101 = 8n1
// DB5 = transmit ctrl 0 00 = -RTS low xmit irq disable
// DB6 = transmit ctrl 1 01 = -RTS low xmit irq enable
//                       10 = -RTS high xmit irq disable
//                       11 = -RTS low, xmit a break lvl, irq disable
// DB7 = receive irq enable
//
void Acia::control_write(BYTE data)
{
	int val;
	if (m_ubControl == data)
	{
		//no change
		return;
	}
	m_ubControl = data;
	switch (data & 0x03)
	{
		case 0:
			// divide by 1
			m_eDivideSel = DivideBy1;
			break; 
		case 1:
			// divide by 16
			m_eDivideSel = DivideBy16;
			break;
		case 2:
			//divide by 64
			m_eDivideSel = DivideBy64;
			break;
		case 3:
			Reset();
			m_bResetting = 1;   //ACIA is paused while in Reset mode
			return;
			break;
	}
	if (m_bResetting)
	{
		m_bResetting = 0;
		m_cStatus = acia_TXEmpty;
	}
	//set word size, parity, stopbits
	val = (data & 0x1C) >> 2;
	m_eWordSel = (eWordSel) val;

	//set RTS, xmit IRQ, Break
	val = (data & 0x60) >> 5;
	m_eXmitCtrl = (eXmitCtrl) val;
	int newRxEnable = (data & '\x80') != 0;
	int newTxEnable = (m_eXmitCtrl == RTSIRQ);

	if ( m_rxIRQenable && !newRxEnable)
	{
		m_rxIRQ = 0;
		if (m_bIRQSet)
			ClearACIAIRQ();
	}

	if ( m_txIRQenable && !newTxEnable)
	{
		m_txIRQ = 0;
		if (m_bIRQSet)
			ClearACIAIRQ();
	}


	m_rxIRQenable = newRxEnable;
	m_txIRQenable = newTxEnable;

#if _DEBUG
	TRACE(_T("Acia Ctrl: %s\r\n"), (LPCTSTR) GetStatus());
#endif
}

// read last character received.
// reading clears irq read status register
BYTE Acia::data_read()
{
	DWORD dwRead  = 0;
	BYTE mask = 0xFF;
	if (m_bResetting )
	{
		return m_cOut;
	}

	//read operation clear Flags
	m_cStatus &= ~(acia_RXFull | acia_IRQ | acia_PE | acia_FE  | acia_RXO);
	if (m_rxIRQ > 1)
	{
		if (m_txIRQ < 2)
			ClearACIAIRQ();
	}
	m_rxIRQ = 0;

	if (m_bAutoLoad && m_hInFile == INVALID_HANDLE_VALUE && m_nRTickCount <= 0)
	{
		SetFile(TRUE);
		
		//user didn't really want to save output
		if (m_hInFile == INVALID_HANDLE_VALUE)
		{
			//Need to determine how to prevent auto open from occuring
			//at this point
			
			//For now, just set no characters time to 20 seconds
			m_nRTickCount += 20000000L;

		}
	}	
	
	
	if ( m_eWordSel  < EightN2)
		mask = 0x7F;

	
	if (m_hInFile != INVALID_HANDLE_VALUE)
	{
		//pacing is on, return last byte again
		if (m_nRTickCount > 0)
			return (BYTE) (m_cOut & mask);
		
		if (!ReadFile( m_hInFile, &m_cOut, 1, &dwRead, NULL))
		{
			DWORD dwError = GetLastError();
			if (dwError == ERROR_HANDLE_EOF)
			{
				CloseHandle(m_hInFile);
				m_hInFile = INVALID_HANDLE_VALUE;
				m_nRTickCount = 0;
				m_cOut = 0;
			}
			else
			{
				ReportSystemError(dwError);			
			}

		}
		else
		{
			if (m_dwReadBytes)
				m_dwReadBytes--;
			
			if (dwRead == 0)
			{
				//no more characters to read.  Too bad we didn't know this before
				//flagging character ready. return a null character	from now on
				m_cOut = 0;

				CloseHandle(m_hInFile);
				m_hInFile = INVALID_HANDLE_VALUE;

				//Need to determine how to prevent auto open from occuring
				//at this point
				
				//For now, just set no characters time to 20 seconds
				m_nRTickCount += 20000000L;
			}
			else if (m_eSpeed)
			{
				
				//this calculation is wrong since we always wait one
				//byte time after transmitting. Use it until we use CPU cycles
				//to determine rxTime
				m_nRTickCount = m_eSpeed;

			}
			else
			{
				m_nRTickCount = 0;
			}
			if (m_dwReadBytes == 0)
			{
				//last byte, close da file
				CloseHandle(m_hInFile);
				m_hInFile = INVALID_HANDLE_VALUE;
				
				//For now, just set no characters time to 20 seconds
				m_nRTickCount += 20000000L;

			}

			return (BYTE)(m_cOut & mask);
		}
	}
	else
	{
		//no file opened

	}
	
	return 0;
}

// 6850 has 1 byte buffer essentially.
// after output byte has been stuffed into output shift register,
// the TXEmpty flag is set, allowing 1 byte to be written
// The flag remains set until that byte is moved into the shift register
// (*Notes for future implementation)
//
void Acia::data_write(BYTE data)
{
	if (m_bResetting)
		return;

	//leading bit out is bit 0
	if ( m_eWordSel  < EightN2)
		data &= 0x7F; //7bit mode
	
	if (m_bAutoSave && m_hOutFile == INVALID_HANDLE_VALUE)
	{
		SetFile(FALSE);
		
		//user didn't really want to save output
		if (m_hOutFile == INVALID_HANDLE_VALUE)
			m_bAutoSave = FALSE;
	}
	
	if (m_hOutFile != INVALID_HANDLE_VALUE)
	{
		DWORD dwWritten;
		if (!WriteFile(m_hOutFile, &data, 1, &dwWritten, NULL) || dwWritten != 1)
		{
			dwWritten = GetLastError();
			ReportSystemError(dwWritten);
		}

	}

	//set txBuf full status;
	m_cStatus &= ~acia_TXEmpty;
	if (m_txIRQ > 1)
	{
		if (m_rxIRQ < 2)
			ClearACIAIRQ();
	}
	m_txIRQ = 0;


	if (m_eSpeed)
	{
		m_nTTickCount = m_eSpeed;
	}

}

void Acia::SetFile(BOOL bInput /*= TRUE*/)
{
	OPENFILENAME ofn;
	TCHAR file[MAX_PATH];
	TCHAR szTitle[255];
	TCHAR szFilter[MAX_PATH];
	TCHAR Path[MAX_PATH];
	BOOL bOK = FALSE;



	CString str = AfxGetApp()->GetProfileString(ENTRY_OSI, LAST_PATH);	
	
	if (str.GetLength())
	{
		_tcscpy(Path, str);

		int nLen = (int)_tcslen(Path);
		if (Path[nLen-1] != _T('\\'))
		{
			TCHAR *p = _tcsrchr(Path, _T('\\'));
			if (p)
				*(p+1) = _T('\0');
		}

	}
	

	ZeroMemory(&ofn, sizeof(OPENFILENAME));

	_tcscpy(szFilter, _T("All Programs (*.bas;*.hex;*.txt, *.lod, *.65v, *.chk)|*.bas;*.hex;*.txt;*.lod;*.65v;*.chk|All Files (*.*)|*.*|"));
	for (int i=0; i < _MAX_PATH; i++)
	{
		//filter needs null separating paramters, replace | in resource with Null
		if (szFilter[i] == _T('|'))
			szFilter[i] = _T('\0');
	}

	*file = _T('\0');
	*szTitle = _T('\0');

	if (bInput)
	{
		_tcscpy(szTitle,_T("Open File for Serial Port Read"));
	}
	else
	{
		_tcscpy(szTitle, _T("Create File for Serial Port Write"));
	}	
	HWND hWnd = AfxGetMainWnd()->m_hWnd;
	if (g_pOSIWindow && g_pOSIWindow->m_hWnd)
		hWnd =  g_pOSIWindow->m_hWnd;

	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner =  hWnd;
	ofn.hInstance = AfxGetInstanceHandle();
	ofn.Flags = OFN_EXPLORER | (bInput ? OFN_FILEMUSTEXIST : (OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY) );
	ofn.lpstrInitialDir	= Path;
	ofn.lpstrFile = file;
	ofn.nMaxFile = MAX_PATH;
	ofn.lpstrTitle = szTitle;
	ofn.lpstrFilter = szFilter;

	if (bInput)
	{
		bOK = GetOpenFileName( &ofn );
		if (bOK)
		{
		
			m_dwReadBytes = 0;
			if (m_hInFile && m_hInFile != INVALID_HANDLE_VALUE)
			{
				//Close old file first
				CloseHandle(m_hInFile);
				m_hInFile = INVALID_HANDLE_VALUE;
			}
			
			m_hInFile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ,
					NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 
					NULL );

			if (m_hInFile == INVALID_HANDLE_VALUE)
			{
				ReportSystemError(GetLastError());
				return;
			}
			else
			{
				TCHAR drive[_MAX_DRIVE];
				m_szInFile = file;

				_tsplitpath( file, drive, Path,	NULL, NULL);
				_tmakepath(file, drive, Path , NULL, NULL);
				m_nRTickCount = 0;
				m_dwReadBytes = GetFileSize( m_hInFile, NULL);
			}

		}
	}
	else
	{
		bOK = GetSaveFileName( &ofn );
		
		if (bOK)
		{
			if ( m_hOutFile && m_hOutFile != INVALID_HANDLE_VALUE)
			{
				//close old file first
				CloseHandle(m_hOutFile);
				m_hOutFile = INVALID_HANDLE_VALUE;
			}
			
			m_hOutFile = CreateFile(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
					NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 
					NULL );

			if (m_hOutFile == INVALID_HANDLE_VALUE)
			{
				ReportSystemError(GetLastError());
				return;
			}
			else
			{
				TCHAR drive[_MAX_DRIVE];
				_tsplitpath( file, drive, Path,	NULL, NULL);
				_tmakepath(file, drive, Path , NULL, NULL);
				m_nTTickCount = 0;
			}


		}
		 
	}
	if (bOK )
	{
		//save open path
		AfxGetApp()->WriteProfileString(ENTRY_OSI,   LAST_PATH, file);
	}

}

//close file if open
void Acia::CloseFile(BOOL bInput /*=TRUE */)
{
			
	if (bInput)
	{
		if (m_hInFile && m_hInFile != INVALID_HANDLE_VALUE)
		{
			//Close old file first
			CloseHandle(m_hInFile);
			m_hInFile = INVALID_HANDLE_VALUE;
		}
	}
	else
	{
		if ( m_hOutFile && m_hOutFile != INVALID_HANDLE_VALUE)
		{
			//close old file first
			CloseHandle(m_hOutFile);
			m_hOutFile = INVALID_HANDLE_VALUE;
		}

	}

}

CString Acia::GetStatus()
{
	CString strReturn;

    const TCHAR* WORD[] = { _T("7E2"), _T("7O2"),_T("7E1"),_T("7O1"),_T("8N2"),_T("8N1"),_T("8E1"),_T("8O1") };
    const TCHAR* BAUD[] = { _T("/1"), _T("/16"), _T("/64"), _T("RST") };
    const TCHAR* CONT[] = { _T("RTSNOIRQ"), _T("RTSIRQ"), _T("NORTSNOIRQ"), _T("RTSBREAK") };
	
	strReturn.Format(_T("%s %s %s"), WORD[(int)m_eWordSel], BAUD[(int)m_eDivideSel], CONT[(int)m_eXmitCtrl]);
	return strReturn;
}

